﻿using MengCC.Common.Serialize;
using MengCC.Core.Data;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace MengCC.Framework.Data
{
    public class BaseRepository<TEntity, TIdentityType> : IBaseRepository<TEntity, TIdentityType> where TEntity : BaseEntity<TIdentityType>
    {
        private static List<TEntity> data;
        static BaseRepository()
        {
            data = ReadData();
        }

        private static List<TEntity> ReadData()
        {
            var dir = Path.Combine(Environment.CurrentDirectory, "Data");
            if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
            var path = Path.Combine(dir, typeof(TEntity).FullName);
            if (!File.Exists(path)) return new List<TEntity>();
            try
            {
                return JsonConvert.DeserializeObject<List<TEntity>>(File.ReadAllText(path, Encoding.Default));
            }
            catch (Exception)
            {
                return new List<TEntity>();
            }
        }
        private static void WriteData()
        {
            var dir = Path.Combine(Environment.CurrentDirectory, "Data");
            if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
            var path = Path.Combine(dir, typeof(TEntity).FullName);
            File.WriteAllText(path, JsonConvert.SerializeObject(data), Encoding.Default);
        }



        public TEntity Add(TEntity TEntity)
        {
            lock ($"BaseRepository-add-{typeof(TEntity).FullName}")
            {
                TEntity.ModifiedTime = DateTime.Now;
                if (TEntity.Id is int || TEntity.Id is long || TEntity.Id is short)
                {
                    var latestItem = data.OrderByDescending(d => d.Id).FirstOrDefault();
                    if (latestItem == null)
                        TEntity.Id = (TIdentityType)Convert.ChangeType(1, typeof(TIdentityType));
                    else
                    {
                        var temp = (long)Convert.ChangeType(latestItem.Id, typeof(long));
                        TEntity.Id = (TIdentityType)Convert.ChangeType(temp + 1, typeof(TIdentityType));
                    }
                }
                else if (TEntity.Id is Guid && TEntity.Id.Equals(Guid.Empty))
                {
                    TEntity.Id = (TIdentityType)Convert.ChangeType(Guid.NewGuid(), typeof(TIdentityType));
                }
                data.Add(TEntity);
                WriteData();
                return TEntity;
            }

        }

        public int Count(Expression<Func<TEntity, bool>> expression = null)
        {
            return data.Where(d => expression != null ? expression.Compile().Invoke(d) : true).Count();
        }

        public bool Delete(TIdentityType id)
        {
            lock ($"BaseRepository-delete-{typeof(TEntity).FullName}")
            {
                var item = data.Where(d => d.Id.Equals(id)).FirstOrDefault();
                if (item != null)
                {
                    data.Remove(item);
                    WriteData();
                    return true;
                }
            }
            return false;
        }

        public TEntity Get(TIdentityType id)
        {
            return data.Where(d => d.Id.Equals(id)).FirstOrDefault();
        }

        public TEntity Get(Expression<Func<TEntity, bool>> expression = null, Expression<Func<TEntity, object>> orderby = null, bool orderByType = false)
        {
            var temp = data.Where(d => expression != null ? expression.Compile().Invoke(d) : true);
            if(orderby != null )
            {
                if (!orderByType)
                    temp = temp.OrderByDescending(d => orderby.Compile().Invoke(d));
                else
                    temp = temp.OrderBy(d => orderby.Compile().Invoke(d));
            }
            return temp.FirstOrDefault();
        }

        public IEnumerable<TEntity> GetList(Expression<Func<TEntity, bool>> expression = null, Expression<Func<TEntity, object>> orderby = null, bool orderByType = false)
        {
            var temp = data.Where(d => expression != null ? expression.Compile().Invoke(d) : true);
            if (orderby != null)
            {
                if (!orderByType)
                    temp = temp.OrderByDescending(d => orderby.Compile().Invoke(d));
                else
                    temp = temp.OrderBy(d => orderby.Compile().Invoke(d));
            }
            return temp;
        }

        public PaggingEntity<TEntity> GetPaggingList(int pageIndex, int pageSize = 10, Expression<Func<TEntity, object>> columns = null, Expression<Func<TEntity, bool>> where = null, Expression<Func<TEntity, object>> orderby = null, bool orderByType = false)
        {


            return new PaggingEntity<TEntity>
            {
                Items = GetList(where, orderby, orderByType).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(),
                Total = Count(where)
            };
        }

        public bool Update(TEntity TEntity, Expression<Func<TEntity, object>> updateColumns = null)
        {
            lock ($"BaseRepository-update-{typeof(TEntity).FullName}")
            {
                TEntity.ModifiedTime = DateTime.Now;
                var item = data.Where(d => d.Id.Equals(TEntity.Id)).FirstOrDefault();
                if (item != null)
                {
                    data.Remove(item);
                    data.Add(TEntity);
                    WriteData();
                    return true;
                }
                return false;
            }
        }
    }
}
